const puppeteer = require("puppeteer");
const axios = require("axios");
const querystring = require("node:querystring");
const submitByProxy = require("./pinzan");
const logger = require("./utils/log");
const { generateRandomPhoneUa, generateRandomUa } = require("./utils");
let browser;
let warnCount = 0;
const tunnelQueue = [
//  {
//    proxyServer: "t1.juliangip.com:22167",
//    username: "13880213854",
//    password: "n8afz5ym",
//    name: "巨量",
//  },
  {
    proxyServer: 't2.juliangip.com:21698',
    username: '18280133086',
    password: 'xDWQbFY1',
    name: '巨量',
  },
  {
    proxyServer: "tunnel5.qg.net:19300",
    username: "CBC1E6B1",
    password: "F130FF49E91B",
    name: "青果",
  },
  {
    proxyServer: "z543.kdltps.com:15818",
    username: "t10097956075797",
    password: "r9p4vdk7",
    name: "快代理",
  },
  {
    proxyServer: "u10147.5.tn.16yun.cn:6441",
    username: "16ASZOGP",
    password: "598445",
    name: "亿牛云",
  },
];
const SUBMIT_TYPE = {
  WX: "WX",
  PHONE: "PHONE",
  PC: "PC",
};
let currentTunel = tunnelQueue.shift();
const sleep = (time = 1000) => {
  return new Promise((resolve) => setTimeout(() => resolve(), time));
};
const getSign = async (page, retryCount = 3, interval = 1000) => {
  let sign = null;
  await sleep();
  while (retryCount--) {
    try {
      sign = await page.evaluate(() => {
        return getNVCVal();
      });
      break;
    } catch (e) {
      await sleep(interval);
    }
  }
  return sign;
};
const handleProxyData = (data) => {
  const res_str = data.replace("callback(", "").replace(");", "");
  const res_json = JSON.parse(res_str);
  return res_json;
};
const requestIntercept = (interceptedRequest) => {
  if (
    interceptedRequest.url().includes(".png") ||
    interceptedRequest.url().includes(".jpg") ||
    interceptedRequest.url().includes(".jpeg")
  )
    interceptedRequest.abort(); //终止请求
  else interceptedRequest.continue(); //弹出
};
const wjxStep = async (page, url, submitType = SUBMIT_TYPE.PC) => {
  await page.setRequestInterception(true);
  page.on("request", requestIntercept);
  await page.setUserAgent(
    submitType === SUBMIT_TYPE.PC ? generateRandomUa() : generateRandomPhoneUa()
  );
  await page.goto(url, {
    waitUntil: ["networkidle2"],
  });
  await page.evaluate(async function () {
    Object.defineProperty(navigator, "webdriver", { get: () => false });
    window.scrollTo(0, document.body.scrollHeight);
  });
  let ctlError = false;
  // let aError = false;
  let result = true;
  // divNext
  try {
    await page.click("#ctlNext");
  } catch (e) {
    ctlError = true;
    logger.warn(url + "submit button click error!");
  }
  if (ctlError) {
    try {
      await page.click("#divNext a");
    } catch (e) {
      result = false;
      logger.warn(url + "next button click error!");
    }
  }
  // if (ctlError) {
  //   try {
  //     await page.click("a");
  //   } catch (e) {
  //     aError = true;
  //     logger.warn(url + "a button click error!");
  //   }
  // }
  // if (aError) {
  //   try {
  //     await page.click("input");
  //   } catch (e) {
  //     result = false;
  //     logger.warn(url + "input button click error!");
  //   }
  // }
  return result;
};

const createSubmitConf = (sign) => {
  return `https://cf.aliyun.com/nvc/nvcAnalyze.jsonp?a=${sign}&v=0.04&callback=callback`;
};
const StatusMap = {
  NOCLICK: {
    STATUS: 4001,
    MSG: "元素不能点击！",
  },
  NOSIGN: {
    STATUS: 4002,
    MSG: "getNVCVal获取失败!",
  },
  CODEERROR: {
    STATUS: 4003,
    MSG: "程序错误!",
  },
  SUCCESS: {
    STATUS: 4000,
    MSG: "成功！",
  },
};
const HTTP_SUCCESS = 200;
const httpResult = (
  data,
  status = StatusMap.SUCCESS.STATUS,
  message = StatusMap.SUCCESS.MSG,
  code = HTTP_SUCCESS
) => {
  return {
    code,
    message,
    status,
    result: data,
  };
};
let createSignErrorCount = 0;
setInterval(async () => {
  tunnelQueue.push(currentTunel);
  currentTunel = tunnelQueue.shift();
  if (browser) {
    await browser.close();
  }
  await init();
  createSignErrorCount = 0;
  warnCount = 0;
}, 15 * 60 * 1000);
const getSignInfo = async function (url, proxyUrl, workOrderId, submitType) {
  logger.info(
    `[开始-${workOrderId}-${currentTunel.name}-${submitType}]: ${url}`
  );
  if (!url) {
    logger.error(`[进行中-${workOrderId}]：没有链接地址！`);
    return;
  }
  if (!proxyUrl) {
    logger.error(`[进行中-${workOrderId}]: 没有代理地址！`);
  }
  if (!browser) {
    logger.error(`[进行中-${workOrderId}]: 没有浏览器环境!`);
    return;
  }
  const page = await browser.newPage({
    timeout: 0,
  });
  await page.authenticate({
    username: currentTunel.username,
    password: currentTunel.password,
  });
  try {
    const handleRes = await wjxStep(page, url, submitType);
    await page.removeListener("request", requestIntercept);
    if (!handleRes) {
      return httpResult(null, StatusMap.NOCLICK.STATUS, StatusMap.NOCLICK.MSG);
    }
    const sign = await getSign(page);
    if (!sign) {
      logger.error("create sign error");
      createSignErrorCount += 1;
      if (createSignErrorCount > 10) {
        tunnelQueue.push(currentTunel);
        currentTunel = tunnelQueue.shift();
        await browser.close();
        await init();
        createSignErrorCount = 0;
      }
      await page.close();
      return httpResult(null, StatusMap.NOSIGN.STATUS, StatusMap.NOSIGN.MSG);
    }
    const res = await submitByProxy(createSubmitConf(sign), proxyUrl, url);
    if (res.status === 200) {
      const resJson = handleProxyData(res.text);
      // 成功重置
      if (resJson.result?.code === 200) {
        warnCount = 0;
      } else {
        warnCount += 1;
      }
      // 10次没出现，换隧道，关闭当前上下文，开启新隧道的上下文
      if (warnCount >= 10) {
        tunnelQueue.push(currentTunel);
        currentTunel = tunnelQueue.shift();
        await browser.close();
        await init();
        warnCount = 0;
      }
      await page.close();
      return httpResult({
        sign: JSON.parse(querystring.unescape(sign)),
        data: handleProxyData(res.text),
      });
    }
    return;
  } catch (e) {
    logger.error("excute fail", e);
    await page.removeListener("request", requestIntercept);
    await page.close();
    return httpResult(
      null,
      StatusMap.CODEERROR.STATUS,
      e?.message || StatusMap.CODEERROR.MSG
    );
  }
};
const init = async function () {
  // @todo protocolTimeout
  browser = await puppeteer.launch({
    args: [
      "--proxy-server=" + currentTunel.proxyServer + "",
      "--no-sandbox",
      "--disable-setuid-sandbox",
    ],
    slowMo: 30,
  });
};

module.exports = {
  getSignInfo,
  init,
};
